feat(checkout): CHECKOUT-10026 Connect new Google Places API behind feature flag#3142
feat(checkout): CHECKOUT-10026 Connect new Google Places API behind feature flag#3142bc-maxy wants to merge 6 commits into
Conversation
|
cursor review |
|
cursor review |
|
cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit c05b09f. Configure here.
| const onSelectHandler = (item: AutocompleteItem) => { | ||
| const isUsingLegacyApi = () => !isNewPlacesApiEnabled || newGooglePlacesApiState.isUnavailable; | ||
|
|
||
| const finalizeSelection = ( |
There was a problem hiding this comment.
It looks like many lines are added to this file.
Is it possible to move them out as separate file(s) to keep this file small?
There was a problem hiding this comment.
The rest of the PR looks good.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 33ca5c2. Configure here.
| const fetchSuggestionsWithLegacyApi = (input: string): void => { | ||
| const service = googleAutocompleteServiceRef.current; | ||
|
|
||
| if (!service) return; | ||
|
|
||
| service.getAutocompleteService().then((autocompleteService) => { | ||
| autocompleteService.getPlacePredictions( | ||
| { input, types: types || ['geocode'], componentRestrictions }, | ||
| (results) => { | ||
| setItems(toAutocompleteItems(results ?? undefined)); | ||
| }, | ||
| ); | ||
| }); | ||
| }; | ||
|
|
||
| const fetchSuggestionsWithNewApi = (input: string): void => { | ||
| const service = newGooglePlacesApiServiceRef.current; |
There was a problem hiding this comment.
Not blocking, just a thought. Does it makes sense to have 2 classes or similar for old and new aPI and then based on the isUsingLegacyApi we use the correct class in the hook to avoid naming such as fetchSuggestionsWithNewApi/fetchSuggestionsWithLegacyApi

What/Why?
This is part II of this change: #3135, where I actually wire up this new Places API service to production, but hide new functionality behind a feature flag.
When the CHECKOUT-10026.new_google_places_api experiment is on, address autocomplete uses Google's new Places API (session-token based, AutocompleteSuggestion/Place classes) instead of the legacy API service. If the new API is denied due to permissions error (
gRPC PERMISSION_DENIED), we assume that the service is not enabled for the store and silently fall back to the legacy API for the rest of the session. No visible errors for users and no visible change between old and new service - this is an infrastructure swap behind a feature flag.NOTE: when the flag is off, there will be no change for end users, However, the code won't be byte-to-byte identical: we initialize
newGooglePlacesApiServiceRef.currentwithnew NewGooglePlacesApiService(apiKey)unconditionally on mount, even when the flag is off. It has no side effects though (no network call, no script load - the script loader inside of it is lazy), so this is just a trivial extra allocation without any functional change for end users.CHECKOUT-10026.new_google_places_api is currently set to
truein integration and staging andfalsefor everyone in production.Rollout/Rollback
Set CHECKOUT-10026.new_google_places_api experiment to false if the store where we rolled out the feature is affected. For issues with other stores revert the PR.
Testing
New CI tests + manual testing.
If you want to test yourself you can use these API keys in https://console.cloud.google.com/apis/credentials?project=bc-hackathon-env:
My tests are below:
With experiment set to false (same as current production)
Existing customers with old API keys:
Screen.Recording.2026-06-30.at.3.16.03.PM.mov
New customers with new API keys (doesn't work):
Screen.Recording.2026-06-30.at.3.18.15.PM.mov
With experiment set to true
Existing customers with old API keys (Initial 403, then fallback to the old service. Billing step will remember that we fallback and skips new api):
Screen.Recording.2026-06-30.at.3.30.18.PM.mov
New customers with new API keys (just works):
Screen.Recording.2026-06-30.at.3.24.12.PM.mov
Note
Medium Risk
Touches a core checkout address flow and external Google APIs with session-wide fallback behavior; rollout is gated by a feature flag and preserves legacy behavior when off or denied.
Overview
Checkout address line 1 autocomplete can use Google's new Places API when the
CHECKOUT-10026.new_google_places_apiexperiment is on;AddressFormreads that flag and passesisNewPlacesApiEnabledthroughGoogleAutocompleteFormField.Autocomplete logic moves into
useGoogleAutocomplete, which prefers the new service for suggestions and place details and otherwise uses the legacy Maps Places client. On gRPC permission denied, it falls back to legacy for that request and latches so later keystrokes skip the new API for the rest of the checkout session; other errors clear suggestions or skip selection without switching APIs. With the flag off, only legacy runs. When the new path is active, legacy shares the same Maps script loader to avoid loading the JS API twice.GoogleAutocompleteis thinned to the shared UI hook; legacy prediction mapping lives inutils.ts. New component tests cover the happy path, permission fallback/latching, transient failures, and flag-off behavior.Reviewed by Cursor Bugbot for commit d23c96d. Bugbot is set up for automated code reviews on this repo. Configure here.